//////////////////////////////////////////////////////////////////////////
//
// Vector Graph Core2D
//
// Module:
//        ClosestPoint2D.cs
//
// Description:
//        Implementation of computing closest point from a given
//            test point to a graph element
//
// Author:
//        Created on 2008.04.17 by Destrayer
//
//////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Text;

namespace VectorGraph.Core2D
{
    class ClosestPoint2D
    {
        public static Point2d ClosestPoint(Rectangle2d rect, Point2d testPt)
        {
            Point2d ret = rect.LeftBottom;
            if (Math.Abs(testPt.X - rect.Left) > Math.Abs(testPt.X - rect.Right))
            {
                ret.X = rect.Right;
            }
            if (Math.Abs(testPt.Y - rect.Bottom) > Math.Abs(testPt.Y - rect.Top))
            {
                ret.Y = rect.Top;
            }
            return ret;
        }

        public static Point2d ClosestPoint(Beeline2d bl, Point2d testPt)
        {
            Point2d ret = bl.Direction;
            double rate = ret.DotProduct(testPt - bl.BasePt) / ret.DotProduct(ret);
            ret.Scale(rate);
            ret += bl.BasePt;
            return ret;
        }

        public static Point2d ClosestPoint(Circle2d c, Point2d testPt)
        {
            Point2d ret = testPt - c.CenterPt;
            ret.Scale(c.Radius / ret.Length);
            ret += c.CenterPt;
            return ret;
        }

        public static Point2d ClosestPoint(Segment2d seg, Point2d testPt)
        {
            // need modify if not stable at horizontal or vertical segment
            Point2d ret = ClosestPoint2D.ClosestPoint(seg.ToBeeline(), testPt);
            Point2d v1 = ret - seg.StartPt;
            Point2d v2 = ret - seg.EndPt;
            if (v1.X * v2.X > 0 || v1.Y * v2.Y > 0)
            {
                if (Math.Abs(v1.X * v1.Y) < Math.Abs(v2.X * v2.Y))
                {
                    ret = v1;
                }
                else
                {
                    ret = v2;
                }
            }
            return ret;
        }

        public static Point2d ClosestPoint(Arc2d arc, Point2d testPt)
        {
            double angle = arc.StartVector.SweepAngle(testPt - arc.CenterPt);
            double sweepAngle = arc.SweepAngle;
            if (angle > sweepAngle / 2 + Math.PI)
            {
                return arc.CenterPt + arc.StartVector;
            }
            if (angle > sweepAngle)
            {
                return arc.CenterPt + arc.EndVector;
            }
            return ClosestPoint2D.ClosestPoint(arc.ToCircle(), testPt);
        }

        public static Point2d ClosestPoint(Path2d path, Point2d testPt)
        {
            Point2d pt, ret = testPt;
            double dist, minDist = 0;
            for (int i = 0; i < path.EdgeCount; i++)
            {
                pt = ClosestPoint2D.ClosestPoint(path.Edge(i), testPt);
                dist = pt.SqrDistance(testPt);
                if (i == 0 || dist < minDist)
                {
                    ret = pt;
                    minDist = dist;
                }
            }
            return ret;
        }
    }
}
